/*
 *	Copyright (C) 2008 - Mauricio Bieze Stefani
 *	This file is part of the Insano Game Engine.
 *
 *	Insano is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	Insano is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with Insano.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "MapManager.h"
#include "MBSTrace.h"

using namespace irr;
using namespace core;
using namespace scene;
using namespace io;

int MapManager::initMapLoader(const char *mapDefinitionXml)
{
	int rc=0;

	// Load file

	if (!mapDefsXmlDoc.LoadFile(mapDefinitionXml))
	{
		TRACE_ERROR("TiXmlDocument::LoadFile(%s)", mapDefinitionXml);
		rc=-1;
	}
	else
	{
		TiXmlHandle hDoc(&mapDefsXmlDoc);

		// Loop over all filesystem defs

		for(TiXmlElement *fs = hDoc.FirstChild("Filesystem").Element(); fs; fs=fs->NextSiblingElement("Filesystem"))
		{
			if(NULL==fs->Attribute("File"))
			{
				TRACE_ERROR("Filesystem attribute \'File\' not found");
				rc=-1;
			}
			else
			{
				const char *filename=fs->Attribute("File");

				if( (NULL==fs->Attribute("Type")) || (strcmp(fs->Attribute("Type"),"zip")!=0) )
				{
					TRACE_WARN("File %s has invalid \'Type\' attribute. Using zip as default", filename);
					rc=-1;
				}

				if(device->getFileSystem()->addZipFileArchive(filename))
					TRACE_INFO("added %s to virtual filesystem", filename);
				else
				{
					TRACE_ERROR("trying to add %s to virtual filesystem", filename);
					rc=-1;
				}
			}
		}
	}
	return rc;
}

/*
	Loads and return a pointer (non-exclusive) to the requested map
*/
Map* MapManager::loadMap(irr::scene::ISceneManager *smgr, std::string mapname)
{
	TiXmlHandle hDoc(&mapDefsXmlDoc);

	// loop over all available Map nodes
	for(TiXmlElement *mapElement = hDoc.FirstChild("Map").Element(); mapElement; mapElement=mapElement->NextSiblingElement("Map"))
	{
		const char *charName = mapElement->Attribute("Name");

		// is this the one we want?
		if((charName) && (strcmp(charName,mapname.c_str())==0))
		{
			const char *file = mapElement->Attribute("File");
			if(file)
			{
				Map *newmap = new Map();
				newmap->smgr = smgr;

				const char *format = mapElement->Attribute("Format");

				if((format) && strcmp(format,"irr")==0)
				{
					smgr->loadScene(file);

					IMetaTriangleSelector* metaSelector = smgr->createMetaTriangleSelector();

					fillMetaTriangleSelector(smgr, smgr->getRootSceneNode(), metaSelector );

					newmap->selector=metaSelector;

					// success
					return newmap;
				}
				else // default is bsp which use getMesh()
				{
					// load the file
					if( (newmap->mesh = smgr->getMesh(file)) )
					{
						// create octree node
						if( irr::scene::ISceneNode* node = smgr->addOctTreeSceneNode(newmap->mesh->getMesh(0)))
						{
							// triangle selector (used for collision)
							newmap->selector = smgr->createOctTreeTriangleSelector( newmap->mesh->getMesh(0), node, 128);

							// success
							return newmap;
						}
					}
				}

				delete newmap;
			}
			else
				TRACE_ERROR("Map attribute File not found");
		}
	}
	return NULL;
}

/*
	Get a Vector 3D child property of the current TiXmlElement in the format:

	< parent >
		<vector3d name=propName value= "1.0, 1.0, 1.0" />
	< /parent >
*/
int MapManager::readVector(TiXmlElement *parent, const char *propName, irr::core::vector3df& dest)
{
	//both must be valid
	if( (parent==NULL) || (propName==NULL) )
		return -1;

	TiXmlHandle h(parent);
	for(TiXmlElement *el=h.FirstChild("vector3d").Element() ; el ; el=el->NextSiblingElement("vector3d") )
	{
		// is this the one we want?
		if(strcmp(el->Attribute("name"),propName)==0)
		{
			int i;
			char temp[37]; // no reason for this specific size ;-)
			const char *pos = el->Attribute("value");

			if(pos==NULL)
			{
				TRACE_ERROR("could not read %s value", propName);
				return -1;
			}

			if(*pos)
			{
				for(i=0 ; *pos && (*pos != ',') ; i++, pos++)
					temp[i] = *pos;
				if(i)
				{
					temp[i]=0;
					dest.X=atof(temp);
				}
			}

			if(*pos)
			{
				pos++;
				for(i=0 ; *pos && (*pos != ',') ; i++, pos++)
					temp[i] = *pos;
				if(i)
				{
					temp[i]=0;
					dest.Y=atof(temp);
				}
			}

			if(*pos)
			{
				pos++;
				for(i=0 ; *pos && (*pos != ',') ; i++, pos++)
					temp[i] = *pos;
				if(i)
				{
					temp[i]=0;
					dest.Z=atof(temp);
				}
			}
			//TRACE_INFO("readVector(%s, (%f, %f, %f))", propName, dest.X, dest.Y, dest.Z);
			return 0;
		}
	}
	//TRACE_ERROR("Unknown vector %s", propName);
	return -1;
}

/*
	This function grabs oct tree nodes from an IrrMesh file and fills a
	triangle selector.

	From sample available at:
	http://www.irrlicht3d.org/wiki/index.php?n=Main.LoadingAnIRRSceneMadeFromIRREditAndApplyingCollision

	Use like this:
	...
		smgr->loadScene("map.irr");
		IMetaTriangleSelector* metaSelector = smgr->createMetaTriangleSelector();
		fillMetaTriangleSelector( smgr->getRootSceneNode(), metaSelector );
	...
*/
int MapManager::fillMetaTriangleSelector(irr::scene::ISceneManager *smgr, ISceneNode* node, IMetaTriangleSelector* meta)
{
	if (node->getType() == ESNT_TERRAIN)
	{
		scene::ITriangleSelector* selector = smgr->createTerrainTriangleSelector((ITerrainSceneNode*)node);
		node->setTriangleSelector(selector);
		meta->addTriangleSelector(selector);
		selector->drop();
	}
	else if (node->getType() == ESNT_OCT_TREE)
	//else if ((node->getType() == ESNT_OCT_TREE) || (node->getType() == ESNT_MESH))
	{
		io::IAttributes* attribs = device->getFileSystem()->createEmptyAttributes();
		if (attribs)
		{
			// get the mesh name out
			node->serializeAttributes(attribs);
			core::stringc name = attribs->getAttributeAsString("Mesh");
			attribs->drop();
			// get the animated mesh for the object
			scene::IAnimatedMesh* mesh = smgr->getMesh(name.c_str());
			if (mesh)
			{
				//create a selector for this mesh and add to the collection
				scene::ITriangleSelector* selector;
//				if (node->getType() == ESNT_OCT_TREE)
					selector = smgr->createOctTreeTriangleSelector(mesh->getMesh(0), node, 128);
//				else //if (node->getType() == ESNT_MESH)
//					selector = smgr->createTriangleSelector(mesh->getMesh(0), node);
				node->setTriangleSelector(selector);
				meta->addTriangleSelector(selector);
				selector->drop();
			}
		}
	}
	else if (node->getType() == ESNT_MESH)
	{
		scene::ITriangleSelector* selector = smgr->createTriangleSelectorFromBoundingBox(node);
		node->setTriangleSelector(selector);
		meta->addTriangleSelector(selector);
		selector->drop();
	}
//	else
//		TRACE_INFO("Unhandled node type=%X", node->getType());

	// now recurse on children...
	core::list<scene::ISceneNode*>::ConstIterator it = node->getChildren().begin();
	core::list<scene::ISceneNode*>::ConstIterator end = node->getChildren().end();

	for (; it != end; it++)
		fillMetaTriangleSelector(smgr, *it, meta);
	return 0;	//TODO: error handling
}
